package com.zhengshang.common.oss;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;

import com.aliyun.oss.OSS;
import com.aliyun.oss.model.AbortMultipartUploadRequest;
import com.aliyun.oss.model.CompleteMultipartUploadRequest;
import com.aliyun.oss.model.InitiateMultipartUploadRequest;
import com.aliyun.oss.model.InitiateMultipartUploadResult;
import com.aliyun.oss.model.PartETag;
import com.aliyun.oss.model.UploadPartRequest;
import com.aliyun.oss.model.UploadPartResult;

/**
 * @ClassName: MultipartUpload 
 * @Description: 文件分片上传处理类
 * @author HY
 * @date 2018年4月25日 下午6:05:26 
 * @version V1.0
 */
public class MultipartUpload {

	private final Logger logger = Logger.getLogger(MultipartUpload.class);

	private OSS client = null;
	private String objectID;
	private String bucketName;
	//线程池
	private ExecutorService executorService = Executors.newFixedThreadPool(50);
	private List<PartETag> partETags = Collections.synchronizedList(new ArrayList<PartETag>());

	MultipartUpload(OSS client, String objectID, String bucketName) {
		this.client = client;
		this.objectID = objectID;
		this.bucketName = bucketName;
	}

	/**
	 * @Title: sliceUploadToAliyun
	 * @Description: 分片上传
	 * @Author HY
	 * @Date 2018年4月23日 上午11:58:24
	 * @Return
	 */
	String sliceUploadToAliyun(byte[] byteArray, long fileLength) {
		String uploadId = "";
		try {

			uploadId = claimUploadId();
            //计算分片的数量
			long partSize = OssUtils.MAX_PART_SIZE;
			int partCount = (int) (fileLength / partSize);
			if (fileLength % partSize != 0) {
				partCount++;
			}

			//分片的片数不能超过10000
			if (partCount > 10000) {
				throw new RuntimeException("上传片数不能超过10000");
			}

			//多线程上传
			for (int j = 0; j < partCount; j++) {
				long startPos = j * partSize;
				long curPartSize = (j + 1 == partCount) ? (fileLength - startPos) : partSize;
				executorService.execute(
						new PartUploader(byteArray, startPos, curPartSize, j + 1, uploadId));
			}

			//关闭线程池
			executorService.shutdown();
			
			//等待完成所有分片
			while (!executorService.isTerminated()) {
				try {
					executorService.awaitTermination(5, TimeUnit.SECONDS);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}

			//完成分片
			completeMultipartUpload(uploadId);

		} catch (Exception e) {
			e.printStackTrace();
			logger.error("OSS分片上传失败，"+e.getMessage());
			if (StringUtils.isNotBlank(uploadId)) {//分片上传失败的同时，将其取消
				canceUpload(uploadId);
			}
		}
		return objectID;
	}
	
	/**
	 * @ClassName: PartUploader 
	 * @Description: 内部类，分片使用
	 * @author HY
	 * @date 2018年4月25日 下午6:04:57 
	 * @version V1.0
	 */
	private class PartUploader implements Runnable {

		private byte[] byteArray;//文件流转化成的数组
		private long startPos;//分片起始位置

		private long partSize;//片数
		private int partNumber;//分片号
		private String uploadId;//ID

		public PartUploader(byte[] byteArray, long startPos, long partSize, int partNumber, String uploadId) {
			this.byteArray = byteArray;
			this.startPos = startPos;
			this.partSize = partSize;
			this.partNumber = partNumber;
			this.uploadId = uploadId;
		}

		public void run() {
			InputStream inputStream;
			try {
				inputStream = new ByteArrayInputStream(byteArray);
				inputStream.skip(this.startPos);//设置流的起始位置

				UploadPartRequest uploadPartRequest = new UploadPartRequest();
				uploadPartRequest.setBucketName(bucketName);
				uploadPartRequest.setKey(objectID);
				uploadPartRequest.setUploadId(this.uploadId);//设置uploadId，一个文件的所有分片的uploadId都一样
				uploadPartRequest.setInputStream(inputStream);//文件流
				uploadPartRequest.setPartSize(this.partSize);//总片数
				uploadPartRequest.setPartNumber(this.partNumber);//分片号

				UploadPartResult uploadPartResult = client.uploadPart(uploadPartRequest);
				synchronized (partETags) {
					partETags.add(uploadPartResult.getPartETag());
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

	/**
	 * @Title: claimUploadId
	 * @Description: 获取uploadId，来自于initiateMultipartUpload
	 * @Author HY
	 * @Date 2018年4月25日 下午6:25:37
	 * @return
	 * @Return
	 * @Throws
	 */
	private String claimUploadId() {
		InitiateMultipartUploadRequest request = new InitiateMultipartUploadRequest(bucketName, objectID);
		InitiateMultipartUploadResult result = client.initiateMultipartUpload(request);
		return result.getUploadId();
	}
	
	/**
	 * @Title: canceUpload
	 * @Description: 取消分片上传，同时已经上传的Part数据也会被删除
	 * @Author HY
	 * @Date 2018年4月25日 下午6:27:38
	 * @param uploadId
	 * @Return
	 */
	private void canceUpload(String uploadId){
		try {
			// 取消分片上传，其中uploadId来自于initiateMultipartUpload
			AbortMultipartUploadRequest abortMultipartUploadRequest = 
			        new AbortMultipartUploadRequest(bucketName, objectID, uploadId);
			client.abortMultipartUpload(abortMultipartUploadRequest);
		} catch (Exception e) {
			logger.error("取消分片上传失败，"+e.getMessage());
		}
	}

	/**
	 * @Title: completeMultipartUpload
	 * @Description: 完成分片
	 * @Author HY
	 * @Date 2018年4月25日 下午6:20:13
	 * @param uploadId
	 * @Return
	 */
	private void completeMultipartUpload(String uploadId) {
		Collections.sort(partETags, new Comparator<PartETag>() {
			public int compare(PartETag p1, PartETag p2) {
				return p1.getPartNumber() - p2.getPartNumber();
			}
		});

		CompleteMultipartUploadRequest completeMultipartUploadRequest = new CompleteMultipartUploadRequest(bucketName,
				objectID, uploadId, partETags);
		client.completeMultipartUpload(completeMultipartUploadRequest);
	}

}
